package it.uni.bo.monitoring.protocol.unmarshaller.analysis;

import it.uni.bo.monitoring.mapping.hibernate.WsOperationPar;
import it.uni.bo.monitoring.protocol.unmarshaller.EventInfo;
import it.uni.bo.monitoring.protocol.unmarshaller.EventParameterInfo;

import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;


import static it.uni.bo.monitoring.service.StaticFields.NUM_PAR_B;
import static it.uni.bo.monitoring.service.StaticFields.PAR_B;
import static it.uni.bo.monitoring.service.StaticFields.QUERY_PAR;

public class ParametersControl implements Validation {
	
	private Session session;
	private SessionFactory factory;
	private int numPar = 0;
	private Logger l;
	private static final String ERROR_NUM_PAR="numero di parametri <= 0 oppure \nnumero di byte dei parametri <= della lunghezza richiesta";
	private static final String ERROR_PAR_INSTANCE="un parametro ha istanza differente da quella richiesta (WsOperationPar)";
	private static final String ERROR_BYTE="errore nella lettura dei byte del parametro";
	private static final String ERROR_NUM_PAR2="numero di parametri <=0 oppure diverso dal numero richiesto";
	
	public ParametersControl(SessionFactory factory) {
		l = Logger.getLogger(this.getClass());
		this.factory=factory;
	}

	public boolean validate(EventInfo ei) {
		byte[] par = ei.getDataObject().getParams();
		
		boolean ok=true;
		if(par!=null){
			session = factory.openSession();
			ok=		numParControl(par) 	&&
					parControl(ei,par);
		if(session!=null){
			if (session.isOpen()) 
				session.close(); }
		}
		//l.info("ParametersControl: "+ok);
		return ok;
	}
	
	private boolean numParControl(byte[] par) {
		ByteBuffer bb = ByteBuffer.allocate(4);
		int len = integerControl(par, bb, 0);
		if((len>0)&& (par.length>NUM_PAR_B+(len*PAR_B))){
			numPar=len;
			return true;
		}
		l.error(ERROR_NUM_PAR+"\n("+len+", "+ par.length+ ", "+(NUM_PAR_B+(len*PAR_B))+")");
		return false;
	}

	private boolean parControl(EventInfo ei,byte[] par) {
		ByteBuffer bb = ByteBuffer.allocate(4);
		boolean result=true;
		Set<EventParameterInfo> setEvent = new HashSet<EventParameterInfo>(0);
		Query query = session.createQuery(QUERY_PAR);
		query.setLong(0, ei.getIdWso());
		query.setCharacter(1, 'y');
		//l.info("Hibernate: "+QUERY_PAR+" [ "+ei.getIdWso()+" 'y' ]");
		List list=query.list();
		if(list.size()>0 && list.size()==numPar){
			int offset=NUM_PAR_B;
			int i=0;
			while(i<list.size() && result){
					byte[] tmp=getFieldContent(par, bb, offset, PAR_B);
					if(tmp!=null){
						Object ob = list.get(i);
						if(ob instanceof WsOperationPar){
							WsOperationPar wop = (WsOperationPar)ob;
							if(wop.getPosition()==i){
								EventParameterInfo ep = new EventParameterInfo(ei.getIdE(), wop, new String(tmp), i);
								setEvent.add(ep);
							}
						}else{
							result=false;
							l.error(ERROR_PAR_INSTANCE);
						}
						offset+=PAR_B+tmp.length;
					}else{
						result=false;
						l.error(ERROR_BYTE);
					}
				i++;
			}
			
		}else{
			result=false;
			l.error(ERROR_NUM_PAR2+"\n("+list.size()+", "+numPar+")");
		}
		if(result)
			ei.setEventParameters(setEvent);
		return result;
	}




	private byte[] getFieldContent(byte[] b, ByteBuffer bb, int offset, int ByteDim){
		int len=0;

		if(ByteDim==4)
			len=integerControl(b, bb,offset);
		if(len>0){
			byte[] b2= new byte[len];
			offset+=ByteDim;
			for(int i=offset;i<len+offset;i++)
				b2[i-offset]=b[i];
			return b2;
		}
		return null;
	}
	
	
	private int integerControl(byte[] b, ByteBuffer bb, int offset){
		bb.clear();
		for(int i=offset;i<offset+4;i++)
			bb.put(b[i]);
		bb.flip();
		int length = bb.getInt();
		if(length>0)
			return length;
		return 0;
	}

}
